﻿module Sustainalytics.Routing {

    import Claims = Sustainalytics.Claims;
    import Authentication = Sustainalytics.Authentication;

    export class RoutingServiceProviderBase {

        protected routeInfoMap: RouteInfoMap = new RouteInfoMap();


        constructor() {
        }


        public createRouteResolver(routeSecurity: RouteSecurity, path: string, requires: string[]): any {
            var ctx = this;

            var resolveFunc = (
                $rootScope: any,
                $q: ng.IQService,
                $timeout: ng.ITimeoutService,
                RoutingService: IRoutingService,
                AuthenticationService: Authentication.IAuthenticationService,
                staticObjects: Common.IStaticObjects,
                $location: ng.ILocationService) => {

                var def = $q.defer();

                // initially the route is not valid
                //$rootScope.isRouteValid = false;

                // try refreshing the token
                var refreshTokenPromise = ctx.tryRefreshToken(
                    $q, $timeout, $rootScope, staticObjects, AuthenticationService);

                refreshTokenPromise['finally'](
                    // success
                    function () {

                        // resolve route requirements
                        var reqPromise = ctx.resolveRouteRequirements($q, path, requires);

                        reqPromise['finally'](function () {

                            // validate route
                            var securityPromise = ctx.validateRoute(routeSecurity, path, $rootScope, $q, RoutingService, AuthenticationService, $location);

                            securityPromise['finally'](
                                // success
                                function () {
                                    def.resolve();
                                });
                        });
                    });

                return def.promise;
            };

            resolveFunc.$inject = ['$rootScope', '$q', '$timeout', 'RoutingService', 'AuthenticationService', 'staticObjects', '$location'];

            return resolveFunc;
        }

        // create service to refresh token if needed
        private tryRefreshToken(
            $q: ng.IQService,
            $timeout: ng.ITimeoutService,
            $rootScope: Authentication.RootScope,
            staticObjects: Common.IStaticObjects,
            AuthenticationService: Authentication.IAuthenticationService): ng.IPromise<any> {

            var def = $q.defer();

            var checkRefreshToken = () => {

                AuthenticationService.refreshToken().then(
                    // success
                    function () {
                        //if ($rootScope.userInfo && $rootScope.userInfo.isAuthenticated) {
                        //    staticObjects.initSignalR();
                        //}
                        def.resolve();
                    },
                    // error
                    function () {
                        def.reject();
                    });
            };

            if (!$rootScope.authData) {
                checkRefreshToken();
            }
            else {
                if (!$rootScope.authData.access_token || staticObjects.authDataIsExpired($rootScope.authData)) {
                    checkRefreshToken();
                }
                else {
                    def.resolve();
                }
            }

            return def.promise;
        }

        // resolve route requirements
        private resolveRouteRequirements($q: ng.IQService, path?: string, requires?: string[]): ng.IPromise<any> {

            if (!requires) {
                return $q.when(null);
            }

            var def = $q.defer();

            var reqModules = $.map(requires, req => {
                return 'modules/Module/ModuleLoader!' + req;
            });

            require(reqModules,
                // success
                function () {
                    def.resolve();
                },
                // error
                function () {
                    def.reject();
                });

            return def.promise;
        }

        // create service to validate route security
        private validateRoute(
            routeSecurity: RouteSecurity,
            path: string,
            $rootScope: any,
            $q: ng.IQService,
            RoutingService: IRoutingService,
            AuthenticationService: Authentication.IAuthenticationService,
            $location: ng.ILocationService): ng.IPromise<any> {

            var def = $q.defer();

            var isDefaultRoute = RoutingService.isDefaultRoute(path);

            if (!routeSecurity) {
                if (!$rootScope.userInfo || !$rootScope.userInfo.isAuthenticated) {
                    $rootScope.isRouteValid = false;

                    if (isDefaultRoute) {
                        AuthenticationService.logout();
                    }
                    else {
                        AuthenticationService.logout($location.path(), null);
                    }

                    def.reject();
                }
                else {
                    $rootScope.isRouteValid = true;
                    def.resolve();
                }
            }
            else {

                if (
                    $location.path() !== '/login'
                    && $location.path() !== '/forgotPassword'
                    && $location.path() !== '/forgotPasswordConfirmation'
                    && $location.path().indexOf('/setPassword') === -1
                    && $location.path() !== '/passwordExpired'
                    && $location.path() !== '/passwordChangeConfirmation'
                    && $location.path()!=='/iplogin'
                    ) {
                    if (!$rootScope.userInfo || !$rootScope.userInfo.isAuthenticated) {
                        AuthenticationService.logout($location.path(), null, routeSecurity.noRedirect);
                    }
                }

                // logout if requested
                if (routeSecurity.logout && $rootScope.userInfo && $rootScope.userInfo.isAuthenticated) { 
                    AuthenticationService.logout(null, null, routeSecurity.noRedirect);
                }

                var validatePromise: ng.IPromise<boolean> = RoutingService.validateRouteSecurity(routeSecurity);

                validatePromise.then(
                    // success
                    function (isValid: boolean) {

                        if (!isValid) {

                            if (routeSecurity.notAllowedAction) {
                                var actionPromise = routeSecurity.notAllowedAction(path, routeSecurity);

                                if (actionPromise) {
                                    actionPromise.then(
                                        // success
                                        function () {
                                            def.resolve();
                                        },
                                        // error
                                        function () {
                                            def.reject();
                                        });
                                }
                                else {
                                    def.resolve();
                                }
                            }
                        }
                        else {
                            def.resolve();
                        }

                        $rootScope.isRouteValid = isValid;
                    },
                    // error
                    function () {
                        def.reject();
                        $rootScope.isRouteValid = false;
                    });
            }

            return def.promise;
        }
    }
} 